﻿//
// Copyright (c) 2011 Western Data Solutions (www.westerndatasolutions.com) (Armen Shimoon)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without 
// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 
// the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Collections.Generic;

namespace WDS.Bencode
{   
    public class BencodeDictionary : BencodeDictionary<BencodeElement> {}
    /// <summary>
    /// A bencoded dictionary that supports the <c>IDictionary&lt;<c>BencodeString</c>,<c>BencodeElement</c>&gt; interface</c>
    /// </summary>
    public class BencodeDictionary<TValue> : BencodeElement, IComparable<BencodeDictionary<TValue>>, IDictionary<BencodeString, TValue> where TValue : BencodeElement
    {
        /// <summary>
        /// Internal representation of the dictionary
        /// </summary>
        private readonly IDictionary<BencodeString, TValue> m_Elements = null;

        /// <summary>
        /// Public accessor for list elements
        /// </summary>
        public IDictionary<BencodeString, TValue> Elements 
        {
            get{return m_Elements;}
        }

        /// <summary>
        /// Tries to return the value from the dictionary, and returns false if the key was not found
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool TryGetValue(BencodeString key, out TValue value)
        {
            return m_Elements.TryGetValue(key, out value);
        }

        /// <summary>
        /// Default property for <c>IDictionary&lt;T&gt; interface</c>
        /// </summary>
        /// <param name="key">Bencoded string key</param>
        /// <returns>Bencoded element value</returns>
        TValue IDictionary<BencodeString, TValue>.this[BencodeString key]
        {
            get { return m_Elements[key]; }
            set { m_Elements[key] = value; }
        }

        /// <summary>
        /// All the keys in the dictionary
        /// </summary>
        public ICollection<BencodeString> Keys
        {
            get { return m_Elements.Keys; }
        }

        /// <summary>
        /// All the values in the dictionary
        /// </summary>
        public ICollection<TValue> Values
        {
            get { return m_Elements.Values; }
        }

        /// <summary>
        /// Default property for <c>BencodeDictionary</c>
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public TValue this[BencodeString key]
        {
            get
            {
                if (m_Elements.ContainsKey(key))
                {
                    return m_Elements[key];
                }
                return default(TValue);
            }
            set
            {
                if (m_Elements.ContainsKey(key))
                {
                    m_Elements[key] = value;
                }
                else
                {
                    m_Elements.Add(key, value);
                }
            }
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        public BencodeDictionary()
        {
            m_Elements = new SortedDictionary<BencodeString, TValue>();
        }

        /// <summary>
        /// Returns true if the key is found in the dictionary
        /// </summary>
        /// <param name="key">The bencoded string key</param>
        /// <returns>The bencoded value</returns>
        public bool ContainsKey(BencodeString key)
        {
            return m_Elements.ContainsKey(key);
        }

        /// <summary>
        /// Add a pair to the dictionary
        /// </summary>
        /// <param name="key">Bencoded string key</param>
        /// <param name="value">Bencoded value</param>
        public void Add(BencodeString key, TValue value)
        {
            if (!m_Elements.ContainsKey(key))
            {
                m_Elements.Add(key, value);
            }
        }

        /// <summary>
        /// Removes a pair from the dictionary
        /// </summary>
        /// <param name="key">The bencoded string key of the pair</param>
        /// <returns>True if the item was removed</returns>
        bool IDictionary<BencodeString, TValue>.Remove(BencodeString key)
        {
            return m_Elements.Remove(key);
        }

        public void Remove(BencodeString key)
        {
            if (m_Elements.ContainsKey(key))
            {
                m_Elements.Remove(key);
            }
        }

        /// <summary>
        /// Compares two bencoded dictionaries
        /// </summary>
        /// <param name="other">The bencoded dictionary to compare against</param>
        /// <returns>1 if different, 0 if same</returns>
        public int CompareTo(BencodeDictionary<TValue> other)
        {
            if (m_Elements == null || other.m_Elements == null)
            {
                return 1;
            }
            if (!m_Elements.Count.Equals(other.m_Elements.Count))
            {
                return 1;
            }
   
            foreach (var dictionaryItem in m_Elements)
            {
                if (!other.m_Elements.ContainsKey(dictionaryItem.Key))
                {
                    return 1;
                }
                int difference = dictionaryItem.Value.CompareTo(other.m_Elements[dictionaryItem.Key]);
                if (difference > 0)
                {
                    return 1;
                }
            }

            return 0;
        }

        /// <summary>
        /// Returns an enumerator for the pairs in this dictionary
        /// </summary>
        /// <returns></returns>
        public IEnumerator<KeyValuePair<BencodeString, TValue>> GetEnumerator()
        {
            return m_Elements.GetEnumerator();
        }

        /// <summary>
        /// Returns a weakly typed enumerator
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Adds a key value pair to the dictionary
        /// </summary>
        /// <param name="item">Strongly typed key-value-pair</param>
        public void Add(KeyValuePair<BencodeString, TValue> item)
        {
            m_Elements.Add(item);
        }

        /// <summary>
        /// Clears the dictionary
        /// </summary>
        public void Clear()
        {
            m_Elements.Clear();
        }

        /// <summary>
        /// Determines whether a pair exists in the dictionary
        /// </summary>
        /// <param name="item">The pair to find</param>
        /// <returns>True if the pair was found</returns>
        public bool Contains(KeyValuePair<BencodeString, TValue> item)
        {
            return m_Elements.Contains(item);
        }

        /// <summary>
        /// Copies the dictionary to an array
        /// </summary>
        /// <param name="array">The output array</param>
        /// <param name="arrayIndex">The index to start at</param>
        public void CopyTo(KeyValuePair<BencodeString, TValue>[] array, int arrayIndex)
        {
            m_Elements.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Removes the pair from the dictionary
        /// </summary>
        /// <param name="item">The pair to remove</param>
        /// <returns>True if the pair was removed</returns>
        public bool Remove(KeyValuePair<BencodeString, TValue> item)
        {
            return m_Elements.Remove(item);
        }

        /// <summary>
        /// The number of entries in the dictionary
        /// </summary>
        public int Count
        {
            get { return m_Elements.Count; }
        }

        /// <summary>
        /// True if the dictionary is read only
        /// </summary>
        public bool IsReadOnly
        {
            get { return m_Elements.IsReadOnly; }
        }

        /// <summary>
        /// Helper for returning a strongly typed representation of a pair value
        /// </summary>
        /// <typeparam name="T">The type to cast to</typeparam>
        /// <param name="key">The bencoded string key</param>
        /// <returns>Strongly typed value, or null bencode object</returns>
        protected T ValueOfType<T>(string key) where T : BencodeElement
        {
            if (!ContainsKey(Bencode.String(key)))
            {
                return (T)NullOf<T>();
            }
            return TryReturnAs<T>(this[Bencode.String(key)]);
        
        }

        /// <summary>
        /// Sets the value at a given key with the given value (non bencoded strings and ints are automatically encoded)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        protected void SetValue<T>(string key, T value)
        {
            if (ContainsKey(Bencode.String(key)))
            {
                this[Bencode.String(key)] = (TValue)BuildElementWithValue(value);
            }
            else
            {
                this.Add(Bencode.String(key), (TValue)BuildElementWithValue(value));
            }
        }


      
    }
}
